/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/interpret/builtins/AstFormationBuilder.h"
#include "simodo/bormental/DrBormental.h"

namespace simodo::interpret::builtins
{
    namespace
    {
        parser::SyntaxDataCollector_null    null_data_collector;
    }

    AstFormationBuilder::AstFormationBuilder(inout::Reporter_abstract & reporter,
                        BaseInterpret_abstract & sbl_host,
                        variable::VariableSet_t & hosts)
        : _m(reporter)
        , _sbl_host(sbl_host)
        , _hosts(hosts)
        , _syntax_data_collector(null_data_collector)
    {
        _ast_formation_module = std::make_shared<AstFormationModule>(_hosts);

        /// \todo Пересмотреть странную передачу самого себя в свой же метод.
        /// PVS Studio: warn V678 An object is used as an argument to its own method.
        /// Consider checking the first actual argument of the 'instantiate' function.
        _sbl_host.importNamespace(u"ast", 
                                  _ast_formation_module->instantiate(_ast_formation_module), 
                                  inout::null_token_location);
    }

    AstFormationBuilder::AstFormationBuilder(inout::Reporter_abstract & reporter,
                        BaseInterpret_abstract & sbl_host,
                        variable::VariableSet_t & hosts,
                        parser::SyntaxDataCollector_interface & syntax_data_collector)
        : _m(reporter)
        , _sbl_host(sbl_host)
        , _hosts(hosts)
        , _syntax_data_collector(syntax_data_collector)
    {
        _ast_formation_module = std::make_shared<AstFormationModule>(_hosts);

        /// \todo Пересмотреть странную передачу самого себя в свой же метод.
        /// PVS Studio: warn V678 An object is used as an argument to its own method.
        /// Consider checking the first actual argument of the 'instantiate' function.
        _sbl_host.importNamespace(u"ast", 
                                  _ast_formation_module->instantiate(_ast_formation_module), 
                                  inout::null_token_location);
    }

    AstFormationBuilder::~AstFormationBuilder()
    {
    }

    bool AstFormationBuilder::weave(const ast::Node & code)
    {
        _sbl_host.inter().addFlowLayer(code);

        loom::FiberStatus status;

        while((status=_sbl_host.inter().executeOperation()) == loom::FiberStatus::Flow)
            ;

        return loom::FiberStatus::Complete == status;
    }

    bool AstFormationBuilder::onStart(const std::map<std::u16string, ast::Node> &handlers)
    {
        if (InterpretState::Flow != _sbl_host.before_start())
            return false;;

        auto it = handlers.find(u"on_Start");

        if (it == handlers.end())
            return true;

        return weave(it->second);
    }

#ifdef AST_BUILDER_DEBUG
    bool AstFormationBuilder::onProduction(size_t ,
                                size_t ,
                                const inout::Token & production,
                                const std::vector<inout::Token> & pattern,
                                const ast::Node & action,
                                size_t ,
                                size_t )
#else
    bool AstFormationBuilder::onProduction(
                                const inout::Token & production,
                                const std::vector<inout::Token> & pattern,
                                const ast::Node & action)
#endif
    {
        if (action.branches().empty())
            return true;

        _ast_formation_module->setCurrentProduction(&production);
        _ast_formation_module->setCurrentPattern(&pattern);

        bool ok = weave(action);

        _ast_formation_module->setCurrentProduction(nullptr);
        _ast_formation_module->setCurrentPattern(nullptr);

        return ok;
    }

    void AstFormationBuilder::onTerminal(const inout::Token & token) 
    {
        _syntax_data_collector.collectToken(token);
    }

    bool AstFormationBuilder::onFinish(bool , const std::map<std::u16string, ast::Node> &handlers)
    {
        _sbl_host.before_finish(InterpretState::Flow);

        auto it = handlers.find(u"on_Finish");

        if (it != handlers.end())
            if (!weave(it->second))
                return false;

        ast().finalize();

        return true;
    }

}